import pygame
import random
import time
import sys
from collections import deque


class Maze:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.maze = [[1 for _ in range(width)] for _ in range(height)]  # 1表示墙，0表示通道
        self.generate_maze(1, 1)  # 从(1,1)开始生成迷宫
        self.start_x, self.start_y = 1, 1
        self.exit_x, self.exit_y = width - 2, height - 2
        self.traps = self.generate_traps()

    def generate_maze(self, cx, cy):
        """使用深度优先搜索生成随机迷宫"""
        self.maze[cy][cx] = 0  # 标记为通道
        directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        random.shuffle(directions)
        for dx, dy in directions:
            nx, ny = cx + 2 * dx, cy + 2 * dy
            if 0 <= nx < self.width and 0 <= ny < self.height and self.maze[ny][nx] == 1:
                self.maze[cy + dy][cx + dx] = 0  # 打通墙壁
                self.generate_maze(nx, ny)

    def generate_traps(self):
        """在迷宫中智能放置陷阱，确保至少有一条无陷阱路径到出口"""
        # 计算可以放置陷阱的位置（通道上且不是起点和终点）
        available_positions = []
        for y in range(1, self.height - 1):
            for x in range(1, self.width - 1):
                if self.maze[y][x] == 0 and (x != self.start_x or y != self.start_y) and \
                        (x != self.exit_x or y != self.exit_y):
                    available_positions.append((x, y))

        # 随机打乱位置，以便随机选择陷阱
        random.shuffle(available_positions)

        # 最多放置通道数的1/4个陷阱，但不超过10个
        max_traps = min(10, len(available_positions) // 4)
        traps = []

        # 逐个尝试添加陷阱，并验证是否仍有路径
        for pos in available_positions:
            if len(traps) >= max_traps:
                break

            # 尝试添加这个陷阱
            traps.append(pos)

            # 检查是否仍有有效路径
            if not self._has_valid_path_with_traps(traps):
                # 如果添加这个陷阱后没有有效路径，则移除它
                traps.pop()

        return traps

    def _has_valid_path_with_traps(self, traps):
        """使用BFS验证在给定陷阱位置的情况下是否存在从起点到终点的有效路径"""
        start_x, start_y = self.start_x, self.start_y
        exit_x, exit_y = self.exit_x, self.exit_y

        # BFS初始化
        visited = [[False for _ in range(self.width)] for _ in range(self.height)]
        queue = deque([(start_x, start_y)])
        visited[start_y][start_x] = True

        directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]

        # BFS遍历
        while queue:
            x, y = queue.popleft()

            # 到达终点
            if x == exit_x and y == exit_y:
                return True

            # 检查四个方向
            for dx, dy in directions:
                nx, ny = x + dx, y + dy
                if 0 <= nx < self.width and 0 <= ny < self.height and \
                        not self.maze[ny][nx] and (nx, ny) not in traps and \
                        not visited[ny][nx]:
                    visited[ny][nx] = True
                    queue.append((nx, ny))

        # 没有找到路径
        return False

    def is_wall(self, x, y):
        """检查坐标是否是墙"""
        return self.maze[y][x] == 1

    def is_trap(self, x, y):
        """检查坐标是否是陷阱"""
        return (x, y) in self.traps

    def is_exit(self, x, y):
        """检查坐标是否是出口"""
        return x == self.exit_x and y == self.exit_y


class Player:
    def __init__(self, maze):
        self.maze = maze
        self.reset_position()
        self.path = [(self.x, self.y)]  # 记录玩家走过的路径

    def reset_position(self):
        """重置玩家位置到起点"""
        self.x = self.maze.start_x
        self.y = self.maze.start_y

    def move(self, dx, dy):
        """移动玩家，如果移动合法则更新位置"""
        new_x, new_y = self.x + dx, self.y + dy
        # 检查是否可以移动（不是墙）
        if 0 <= new_x < self.maze.width and 0 <= new_y < self.maze.height and \
                not self.maze.is_wall(new_x, new_y):
            self.x, self.y = new_x, new_y
            self.path.append((self.x, self.y))  # 记录路径
            # 检查是否踩到陷阱
            if self.maze.is_trap(self.x, self.y):
                self.reset_position()
                self.path = [(self.x, self.y)]  # 重置路径
            # 检查是否到达出口
            return self.maze.is_exit(self.x, self.y)
        return False

    def move_in_direction(self, target_x, target_y):
        """沿着水平或垂直方向移动，直到遇到障碍物"""
        # 计算与目标位置的相对方向
        dx = 0
        dy = 0

        # 优先确定水平或垂直方向
        x_diff = target_x - self.x
        y_diff = target_y - self.y

        if abs(x_diff) > abs(y_diff):
            # 水平方向优先
            dx = 1 if x_diff > 0 else -1 if x_diff < 0 else 0
        else:
            # 垂直方向优先
            dy = 1 if y_diff > 0 else -1 if y_diff < 0 else 0

        # 如果没有方向变化，不移动
        if dx == 0 and dy == 0:
            return False

        # 沿着确定的方向一直移动，直到遇到障碍物
        moved = False
        while True:
            new_x, new_y = self.x + dx, self.y + dy

            # 检查是否超出边界
            if new_x < 0 or new_x >= self.maze.width or new_y < 0 or new_y >= self.maze.height:
                break

            # 检查是否是墙
            if self.maze.is_wall(new_x, new_y):
                break

            # 移动一步
            self.x, self.y = new_x, new_y
            self.path.append((self.x, self.y))
            moved = True

            # 检查是否踩到陷阱
            if self.maze.is_trap(self.x, self.y):
                self.reset_position()
                self.path = [(self.x, self.y)]
                break

            # 检查是否到达出口
            if self.maze.is_exit(self.x, self.y):
                return True

        return moved and self.maze.is_exit(self.x, self.y)


class MazeGame:
    def __init__(self, width=21, height=15, cell_size=30):
        """初始化游戏"""
        pygame.init()
        pygame.font.init()

        self.width = width
        self.height = height
        self.cell_size = cell_size
        self.screen_width = width * cell_size
        self.screen_height = height * cell_size + 70  # 增加额外空间用于显示提示

        self.screen = pygame.display.set_mode((self.screen_width, self.screen_height))
        pygame.display.set_caption("迷宫逃脱游戏")

        self.font = pygame.font.SysFont("SimHei", 24)  # 使用支持中文的字体
        self.clock = pygame.time.Clock()

        self.maze = Maze(width, height)
        self.player = Player(self.maze)
        self.start_time = None
        self.running = True
        self.completed = False
        self.completion_time = 0

    def draw_maze(self):
        """绘制迷宫"""
        for y in range(self.height):
            for x in range(self.width):
                rect = pygame.Rect(x * self.cell_size, y * self.cell_size,
                                   self.cell_size, self.cell_size)
                if self.maze.is_wall(x, y):
                    pygame.draw.rect(self.screen, (50, 50, 50), rect)  # 墙壁：深灰色
                elif self.maze.is_trap(x, y):
                    pygame.draw.rect(self.screen, (255, 0, 0), rect)  # 陷阱：红色
                elif self.maze.is_exit(x, y):
                    pygame.draw.rect(self.screen, (0, 255, 0), rect)  # 出口：绿色
                else:
                    pygame.draw.rect(self.screen, (240, 240, 240), rect)  # 通道：浅灰色

                # 绘制网格线
                pygame.draw.rect(self.screen, (200, 200, 200), rect, 1)

    def draw_player(self):
        """绘制玩家"""
        radius = self.cell_size // 2 - 2
        center = (self.player.x * self.cell_size + radius + 2,
                  self.player.y * self.cell_size + radius + 2)
        pygame.draw.circle(self.screen, (0, 0, 255), center, radius)  # 玩家：蓝色

    def draw_path(self):
        """绘制玩家走过的路径"""
        for i in range(len(self.player.path) - 1):
            x1, y1 = self.player.path[i]
            x2, y2 = self.player.path[i + 1]
            # 计算线段的中心点
            center1 = (x1 * self.cell_size + self.cell_size // 2,
                       y1 * self.cell_size + self.cell_size // 2)
            center2 = (x2 * self.cell_size + self.cell_size // 2,
                       y2 * self.cell_size + self.cell_size // 2)
            pygame.draw.line(self.screen, (0, 0, 150), center1, center2, 3)  # 路径：深蓝色

    def draw_time(self):
        """绘制游戏时间和操作提示"""
        if self.start_time is not None:
            if not self.completed:
                current_time = time.time() - self.start_time
            else:
                current_time = self.completion_time
            time_text = f"时间: {current_time:.2f} 秒"
            text_surface = self.font.render(time_text, True, (0, 0, 0))
            self.screen.blit(text_surface, (10, self.height * self.cell_size + 10))

            # 显示操作提示（分为两行）
            if not self.completed:
                help_text1 = "WASD/方向键移动一步，鼠标点击沿直线移动"
                help_text2 = "按R键重新开始游戏"
            else:
                help_text1 = "恭喜通关！"
                help_text2 = "按R键重新开始游戏"

            help_surface1 = self.font.render(help_text1, True, (0, 0, 0))
            help_surface2 = self.font.render(help_text2, True, (0, 0, 0))

            self.screen.blit(help_surface1, (10, self.height * self.cell_size + 40))
            self.screen.blit(help_surface2, (10, self.height * self.cell_size + 70))

    def handle_events(self):
        """处理游戏事件，支持WASD和鼠标控制"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_r:  # 任何时候按R键都可以重新开始游戏
                    self.reset_game()
                elif not self.completed:  # 只有游戏未完成时才处理移动控制
                    # 方向键控制（只移动一步）
                    if event.key == pygame.K_UP or event.key == pygame.K_w:
                        self.completed = self.player.move(0, -1)
                    elif event.key == pygame.K_DOWN or event.key == pygame.K_s:
                        self.completed = self.player.move(0, 1)
                    elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
                        self.completed = self.player.move(-1, 0)
                    elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                        self.completed = self.player.move(1, 0)

                    # 游戏开始计时
                    if self.start_time is None:
                        self.start_time = time.time()

                    # 如果完成游戏，记录完成时间
                    if self.completed:
                        self.completion_time = time.time() - self.start_time

            # 鼠标点击控制（沿直线移动）
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and not self.completed:
                # 获取鼠标点击位置
                mouse_x, mouse_y = event.pos

                # 计算对应的迷宫格子坐标
                grid_x = mouse_x // self.cell_size
                grid_y = mouse_y // self.cell_size

                # 确保点击在迷宫内
                if 0 <= grid_x < self.width and 0 <= grid_y < self.height:
                    # 沿水平或垂直方向移动
                    self.completed = self.player.move_in_direction(grid_x, grid_y)

                    # 游戏开始计时
                    if self.start_time is None:
                        self.start_time = time.time()

                    # 如果完成游戏，记录完成时间
                    if self.completed:
                        self.completion_time = time.time() - self.start_time

    def reset_game(self):
        """重置游戏"""
        self.maze = Maze(self.width, self.height)
        self.player = Player(self.maze)
        self.start_time = None
        self.completed = False

    def run(self):
        """运行游戏主循环"""
        while self.running:
            self.screen.fill((255, 255, 255))  # 白色背景

            self.draw_maze()
            self.draw_path()
            self.draw_player()
            self.draw_time()

            self.handle_events()

            pygame.display.flip()
            self.clock.tick(60)  # 60 FPS

        pygame.quit()
        sys.exit()


if __name__ == "__main__":
    # 创建并运行游戏，默认大小为21x15，可以根据需要调整
    game = MazeGame(width=21, height=15)
    game.run()
